package com.sali.config;

import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.sali.pojo.PromptLLMCommonModel;
import com.sali.pojo.PromptLLMSafeModel;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: sali
 * @date: 2024/9/1 下午7:11.
 * @version: 1.0
 */
@Component
@ConfigurationProperties(prefix = "semptian.prompt")
public class PromptPropertis {


    private static final Logger logger = LoggerFactory.getLogger(PromptPropertis.class);

    private static final String EXCEL_SUFFIX = ".xlsx";

    // 常量部分
    private static final String LLM_COMMON_SHEET_NAME = "llm_common";
    private static final String LLM_SAFE_SHEET_NAME = "llm_safe";

    private static final List<String> COMMON_COLUMNS = Arrays.asList(
            "aiModelName", "businessName", "promptContent", "lang"
    );

    private static final List<String> SAFE_COLUMNS = Arrays.asList(
            "aiModelName", "businessName", "promptContent", "lang", "className"
    );

    private String filepath;

    public String getFilepath() {
        return filepath;
    }

    public void setFilepath(String filepath) {
        this.filepath = filepath;
    }

    public List<PromptLLMCommonModel> getPromptLLMCommonModels() {
        return loadModels(LLM_COMMON_SHEET_NAME, this::llmCommonModelParse);
    }

    public List<PromptLLMSafeModel> getPromptLLMSafeModels() {
        return loadModels(LLM_SAFE_SHEET_NAME, this::llmSafeModelParse);
    }

    private <T> List<T> loadModels(String sheetName, Function<List<Map<String, Object>>, List<T>> parser) {
        if (StringUtils.isEmpty(filepath)) {
            logger.warn("Prompt file path is empty.");
            return new ArrayList<>();
        }

        File file = new File(filepath);
        if (!file.exists()) {
            logger.warn("Prompt file path does not exist: {}", filepath);
            return new ArrayList<>();
        }

        if (!file.getName().endsWith(EXCEL_SUFFIX)) {
            logger.warn("Prompt file is not an Excel file: {}", filepath);
            return new ArrayList<>();
        }

        try (ExcelReader excelReader = ExcelUtil.getReader(FileUtil.file(file))) {
            List<T> models = new ArrayList<>();
            Workbook workbook = excelReader.getWorkbook();
            Iterator<Sheet> sheetIterator = workbook.sheetIterator();

            while (sheetIterator.hasNext()) {
                Sheet sheet = sheetIterator.next();
                if (sheetName.equalsIgnoreCase(sheet.getSheetName())) {
                    excelReader.setSheet(sheet.getSheetName());
                    List<T> result = parser.apply(excelReader.readAll());
                    if (!CollectionUtils.isEmpty(result)) {
                        models.addAll(result);
                    }
                }
            }
            return models;
        } catch (Exception e) {
            logger.error("Error reading Excel file", e);
            return new ArrayList<>();
        }
    }

    private List<PromptLLMCommonModel> llmCommonModelParse(List<Map<String, Object>> maps) {
        return parseModels(maps, COMMON_COLUMNS, PromptLLMCommonModel::new);
    }

    private List<PromptLLMSafeModel> llmSafeModelParse(List<Map<String, Object>> maps) {
        return parseModels(maps, SAFE_COLUMNS, PromptLLMSafeModel::new);
    }

    private <T> List<T> parseModels(List<Map<String, Object>> maps, List<String> requiredColumns, Supplier<T> modelSupplier) {
        if (CollectionUtils.isEmpty(maps)) {
            return Collections.emptyList();
        }

        return maps.stream()
                .filter(map -> requiredColumns.stream().allMatch(column -> !StringUtils.isEmpty(String.valueOf(map.get(column)))))
                .map(map -> {
                    T model = modelSupplier.get();
                    BeanWrapper beanWrapper = new BeanWrapperImpl(model);
                    requiredColumns.forEach(column -> beanWrapper.setPropertyValue(column, map.get(column)));
                    return model;
                })
                .collect(Collectors.toList());
    }

}
